Skip to content

Conversation

DanBlackwell
Copy link
Contributor

@DanBlackwell DanBlackwell commented Oct 14, 2025

As noted in #161443, currently _longjmp is not intercepted. This results in the callstack being incorrect after a call to _longjmp, and in the worst case overflowing with many calls.

This patch adds the missing interceptor, and a test to make sure that the callstack is correctly maintained.

rdar://162300646

@llvmbot
Copy link
Member

llvmbot commented Oct 14, 2025

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: Dan Blackwell (DanBlackwell)

Changes

As noted in #161443, currently _longjmp is not intercepted. This results in the callstack being incorrect after a call to _longjmp, and in the worst case overflowing with many calls.

This patch adds the missing interceptor, and a test to make sure that the callstack is correctly maintained.

rdar://162300646


Full diff: https://github.com/llvm/llvm-project/pull/163384.diff

2 Files Affected:

  • (modified) compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp (+1-1)
  • (added) compiler-rt/test/tsan/setjmp_longjmp.c (+53)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
index 0c358042d1b56..97a1eed719c84 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
@@ -649,7 +649,7 @@ TSAN_INTERCEPTOR(void, siglongjmp_symname, uptr *env, int val) {
   REAL(siglongjmp_symname)(env, val);
 }
 
-#if SANITIZER_NETBSD
+#if SANITIZER_NETBSD || SANITIZER_APPLE
 TSAN_INTERCEPTOR(void, _longjmp, uptr *env, int val) {
   {
     SCOPED_INTERCEPTOR_RAW(_longjmp, env, val);
diff --git a/compiler-rt/test/tsan/setjmp_longjmp.c b/compiler-rt/test/tsan/setjmp_longjmp.c
new file mode 100644
index 0000000000000..bbc132240c0af
--- /dev/null
+++ b/compiler-rt/test/tsan/setjmp_longjmp.c
@@ -0,0 +1,53 @@
+// RUN: %clang_tsan %s -o %t
+// RUN: %deflake %run %t | FileCheck %s
+
+#include <pthread.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int globalVar;
+
+void foo(jmp_buf *buf, char willExit) {
+  if (willExit)
+    globalVar = 1;
+  _longjmp(*buf, 1);
+}
+
+void func2() {
+  jmp_buf buf;
+  const int reps = 1000;
+  for (int i = 0; i < reps; i++) {
+    if (_setjmp(buf) == 0) {
+      foo(&buf, i == reps - 1);
+    }
+  }
+}
+
+void *writeGlobal(void *ctx) {
+  func2();
+  return NULL;
+}
+
+void *readGlobal(void *ctx) {
+  sleep(1);
+  printf("globalVar: %d\n", globalVar);
+  return NULL;
+}
+
+int main() {
+  pthread_t t[2];
+  pthread_create(&t[0], NULL, writeGlobal, NULL);
+  pthread_create(&t[1], NULL, readGlobal, NULL);
+  pthread_join(t[0], NULL);
+  pthread_join(t[1], NULL);
+  return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK:   Read of size 4 at {{.*}} by thread
+// CHECK:     #0 readGlobal
+// CHECK:   Previous write of size 4 at {{.*}} by thread
+// CHECK:     #0 foo
+// CHECK:     #1 func2
+// CHECK:     #2 writeGlobal

@DanBlackwell
Copy link
Contributor Author

Looks like the added test fails on linux.

As noted in llvm#161443, currently _longjmp is not intercepted. This results in the callstack being incorrect after a call to _longjmp, and in the worst case overflowing with many calls.

This patch adds the missing interceptor, and a test to make sure that the callstack is correctly maintained.
@DanBlackwell DanBlackwell force-pushed the tsan-darwin-longjmp-support branch from 42a3123 to e0e752f Compare October 14, 2025 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants